package cn.lingyangwl.agile.member.service;

import cn.lingyangwl.agile.infra.module.dict.DictItemApi;
import cn.lingyangwl.agile.member.assembly.MemberLevelAssembly;
import cn.lingyangwl.agile.member.constant.MemberCacheKey;
import cn.lingyangwl.agile.member.mapper.MemberLevelMapper;
import cn.lingyangwl.agile.member.model.entity.MemberLevel;
import cn.lingyangwl.agile.member.model.entity.MemberUser;
import cn.lingyangwl.agile.member.model.rqrs.level.MemberLevelSaveReq;
import cn.lingyangwl.agile.member.model.rqrs.level.MemberLevelUpdateReq;
import cn.lingyangwl.agile.model.enums.BoolEnum;
import cn.lingyangwl.framework.lock.constant.LockBeanName;
import cn.lingyangwl.framework.lock.manager.ILockManager;
import cn.lingyangwl.framework.lock.model.ILock;
import cn.lingyangwl.framework.tool.core.exception.BizException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * 会员等级
 *
 * @author shenguangyang
 */
@Slf4j
@Service
public class MemberLevelServiceImpl extends ServiceImpl<MemberLevelMapper, MemberLevel>
    implements IService<MemberLevel>, MemberLevelService {
    
    @Resource
    private MemberLevelAssembly memberLevelAssembly;
    @Resource(name = LockBeanName.REDISSON_SERVICE)
    private ILockManager lockManager;
    @Resource
    private DictItemApi dictItemApi;
    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    
    @Override
    public List<MemberLevel> listLevel() {
        MemberCacheKey cacheKey = MemberCacheKey.LEVEL_DATA;
        @SuppressWarnings("all")
        List<MemberLevel> list = (List<MemberLevel>) redisTemplate.opsForValue().get(cacheKey.getKey());
        if (Objects.nonNull(list)) {
            return list;
        }

        LambdaQueryWrapper<MemberLevel> lqw = MemberLevel.lqw().orderByAsc(MemberLevel::getWeight);
        list = this.list(lqw);

        redisTemplate.opsForValue().set(cacheKey.getKey(), list, cacheKey.getExpire(), cacheKey.getUnit());
        return list;
    }

    @Override
    public MemberLevel getDefaultLevel() {
        LambdaQueryWrapper<MemberLevel> lqw = MemberLevel.lqw().eq(MemberLevel::getIsDefault, true);
        MemberLevel defaultLevel = this.getOne(lqw);
        if (Objects.isNull(defaultLevel)) {
            String key = MemberCacheKey.LEVEL_LOCK.formatKey("default");
            ILock lock = lockManager.getLock(key);
            boolean tryLock = lock.tryLock(5, TimeUnit.SECONDS);
            if (!tryLock) {
                throw new BizException("初始化会员等级失败");
            }
            try {
                defaultLevel = new MemberLevel();
                defaultLevel.setIsDefault(true);
                defaultLevel.setMemberNum(0L);
                defaultLevel.setName("普通会员");
                defaultLevel.setStatus(BoolEnum.TRUE.getCode());
                if (!this.save(defaultLevel)) {
                    throw new BizException("初始化会员等级失败");
                }
            } finally {
                lock.unlock();
            }
        }
        return defaultLevel;
    }

    public MemberLevel getLevelByWeight(String weight, Long excludeId) {
        LambdaQueryWrapper<MemberLevel> lqw = MemberLevel.lqw()
                .eq(MemberLevel::getWeight, weight)
                .ne(Objects.nonNull(excludeId), MemberLevel::getId, excludeId);
        return this.getOne(lqw);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean saveLevel(MemberLevelSaveReq req) {
        MemberCacheKey cacheKey = MemberCacheKey.LEVEL_DATA;
        redisTemplate.delete(cacheKey.getKey());

        // 判断等级是否存在
        MemberLevel dbMemberLevel = getLevelByWeight(req.getWeight(), null);
        if (Objects.nonNull(dbMemberLevel)) {
            throw new BizException("等级已存在");
        }

        // 设置默认等级, 新注册的用户就会使用默认等级
        if (Boolean.TRUE.equals(req.getIsDefault())) {
            req.setStatus(BoolEnum.TRUE.getCode());
            LambdaUpdateWrapper<MemberLevel> luw = MemberLevel.luw().set(MemberLevel::getIsDefault, false);
            this.update(luw);
        }

        MemberLevel entity = memberLevelAssembly.toLevel(req);
    	return this.save(entity);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateLevel(MemberLevelUpdateReq req) {
        MemberCacheKey cacheKey = MemberCacheKey.LEVEL_DATA;
        redisTemplate.delete(cacheKey.getKey());

        // 判断等级是否存在
        MemberLevel dbMemberLevel = getLevelByWeight(req.getWeight(), req.getId());
        if (Objects.nonNull(dbMemberLevel)) {
            throw new BizException("等级已存在");
        }

        // 设置默认等级, 新注册的用户就会使用默认等级
        if (Boolean.TRUE.equals(req.getIsDefault())) {
            LambdaUpdateWrapper<MemberLevel> luw = MemberLevel.luw().set(MemberLevel::getIsDefault, false);
            this.update(luw);
            req.setStatus(BoolEnum.TRUE.getCode());
        }

        MemberLevel entity = memberLevelAssembly.toLevel(req);
    	return this.updateById(entity);
    }

    @Override
    public boolean deleteLevel(Long id) {
        MemberCacheKey cacheKey = MemberCacheKey.LEVEL_DATA;
        redisTemplate.delete(cacheKey.getKey());

    	return this.removeById(id);
    }
    
    @Override
    public boolean batchDeleteLevel(List<Long> ids) {
        MemberCacheKey cacheKey = MemberCacheKey.LEVEL_DATA;
        redisTemplate.delete(cacheKey.getKey());

    	return this.removeByIds(ids);
    }

    @Override
    public MemberLevel getNextLevelIfUpgrade(MemberUser user) {
        if (Objects.isNull(user)) {
            return null;
        }
        MemberLevel memberLevel = this.listLevel()
            .stream().sorted(Comparator.comparing(MemberLevel::getWeight).reversed())
            .filter(e -> Objects.nonNull(e.getUpgrade()))
            .filter(e -> {
                BigDecimal expendMoney = ObjectUtils.defaultIfNull(e.getUpgrade().getExpendMoney(), BigDecimal.ZERO);
                Integer experience = ObjectUtils.defaultIfNull(e.getUpgrade().getExperience(), 0);
                // TODO 判断期望值是否达到目标, 目前不处理金额
                return experience.compareTo(user.getExperience()) < 0;
            }).findFirst().orElse(null);

        if (Objects.nonNull(memberLevel) && memberLevel.getId().equals(user.getLevelId())) {
            return null;
        }
        return memberLevel;
    }
}
